package DyingBaby.backend.dm.pageCache;

import DyingBaby.backend.dm.page.Page;
import DyingBaby.utils.Panic;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import DyingBaby.common.Error;

/**
 * PageCache 接口定义了页面缓存的核心功能，负责管理数据库页面的内存缓存和磁盘I/O。
 * 它是数据库系统的关键组件，通过缓存机制减少磁盘访问，显著提高数据库性能。
 */
public interface PageCache {
    // 页面大小设置为8KB (1 << 13 = 8192 bytes)
    // 这是数据库系统中常见的页面大小，平衡了I/O效率和内存使用
    public static final int PAGE_SIZE = 1 << 13;

    // 创建新页面并分配页号，使用提供的数据初始化页面内容。
    int newPage(byte[] initData);

    // 根据页号获取页面。如果页面已在缓存中，直接返回；否则从磁盘读取并缓存。
    Page getPage(int pgno) throws Exception;

    // 关闭页面缓存，确保所有脏页被刷写到磁盘，释放所有资源。
    void close();

    // 释放页面引用。页面缓存通常使用引用计数机制管理页面生命周期。
    void release(Page page);

    // 截断数据库文件，删除指定页号之后的所有页面，用于空间回收。
    void truncateByBgno(int maxPgno);

    // 获取数据库文件中的总页面数，用于了解数据库大小。
    int getPageNumber();

    // 将指定页面的内容强制刷写到磁盘，确保数据持久化。
    void flushPage(Page pg);

    // 静态工厂方法
    /**
     * 创建全新的数据库文件和页面缓存
     * @param path 数据库文件路径（不含后缀）
     * @param memory 分配给页面缓存的内存大小（字节）
     * @return 页面缓存实例
     */
    public static PageCacheImpl create(String path, long memory) {
        // 创建数据库文件对象（添加.db后缀）
        File f = new File(path+PageCacheImpl.DB_SUFFIX);
        try {
            // 尝试创建新文件，如果文件已存在则报错
            if(!f.createNewFile()) {
                Panic.panic(Error.FileExistsException);
            }
        } catch (Exception e) {
            Panic.panic(e);
        }
        // 检查文件读写权限
        if(!f.canRead() || !f.canWrite()) {
            Panic.panic(Error.FileCannotRWException);
        }

        // 创建RandomAccessFile和FileChannel用于高效I/O
        FileChannel fc = null;
        RandomAccessFile raf = null;
        try {
            // 以读写模式打开随机访问文件
            raf = new RandomAccessFile(f, "rw");
            // 获取文件通道，用于高效的文件I/O操作
            fc = raf.getChannel();
        } catch (FileNotFoundException e) {
           Panic.panic(e);
        }
        // 根据内存大小计算缓存容量（可缓存的页面数）
        return new PageCacheImpl(raf, fc, (int)memory/PAGE_SIZE);
    }

    /**
     * 打开已存在的数据库文件和页面缓存
     * @param path 数据库文件路径（不含后缀）
     * @param memory 分配给页面缓存的内存大小（字节）
     * @return 页面缓存实例
     */
    public static PageCacheImpl open(String path, long memory) {
        // 检查数据库文件是否存在
        File f = new File(path+PageCacheImpl.DB_SUFFIX);
        if(!f.exists()) {
            Panic.panic(Error.FileNotExistsException);
        }
        // 检查文件读写权限
        if(!f.canRead() || !f.canWrite()) {
            Panic.panic(Error.FileCannotRWException);
        }

        // 打开RandomAccessFile和FileChannel
        FileChannel fc = null;
        RandomAccessFile raf = null;
        try {
            // 以读写模式打开随机访问文件
            raf = new RandomAccessFile(f, "rw");
            // 获取文件通道
            fc = raf.getChannel();
        } catch (FileNotFoundException e) {
           Panic.panic(e);
        }
        // 根据内存大小计算缓存容量
        return new PageCacheImpl(raf, fc, (int)memory/PAGE_SIZE);
    }
}
